﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.Security;

namespace PerformanceAssessment.Models
{
    public class Assessment
    {
        public int ID { get; set; }
        [ForeignKey("AssessmentPeriod")]
        public int IdAssessmentPeriod { get; set; }
        [ForeignKey("Employee")]
        public int IdEmployee { get; set; }
        [ForeignKey("Manager")]
        public int IdManager { get; set; }
        public DateTime FromDate { get; set; }
        public DateTime ToDate { get; set; }
        public int IdAssessmentStatus { get; set; }
        public string EmployeeComments { get; set; }
        public string ManagerComments { get; set; }

        public virtual AssessmentPeriod AssessmentPeriod { get; set; }
        public virtual Employee Employee { get; set; }
        public virtual Manager Manager { get; set; }
        public virtual List<Goal> Goals { get; set; }
        public virtual List<SkillAssessment> SkillAssessments { get; set; }

        public Assessment()
        {
        }

        public Assessment(int periodId, int employeeId, int managerId, DateTime fromDate, DateTime toDate, int status, string employeeComments, string managerComments)
        {
            IdAssessmentPeriod = periodId;
            IdEmployee = employeeId;
            IdManager = managerId;
            FromDate = fromDate;
            ToDate = toDate;
            IdAssessmentStatus = status;
            EmployeeComments = employeeComments;
            ManagerComments = managerComments;
        }

        /// <summary>
        /// Executes the stored procedure to create a new assessment and all its dependencies
        /// </summary>
        /// <param name="periodId">Period Id</param>
        /// <param name="employeeId">Employee ID</param>
        /// <param name="managerId">Manager ID</param>
        /// <returns>ID of the created assessment</returns>
        public static int StartNewAssessment(int periodId, int employeeId, int managerId)
        {
            int result;
            using (PerformanceAssessmentDB db = new PerformanceAssessmentDB())
            {
                result = db.Database.SqlQuery<int>("StartNewAssessment @periodId, @employeeId, @managerId, @statusId, @goalsAmount",
                    new SqlParameter("periodId", periodId),
                    new SqlParameter("employeeId", employeeId),
                    new SqlParameter("managerId", managerId),
                    new SqlParameter("statusId", (int)AssessmentStatus.AutoEvaluationOpened),
                    new SqlParameter("goalsAmount", int.Parse(ConfigurationManager.AppSettings["GoalsAmount"]))).FirstOrDefault();
            }
            return result;
        }

        /// <summary>
        /// Checks if there's a difference of more than one point between the scores of the employee and the manager
        /// </summary>
        /// <returns>True if the difference is found</returns>
        public bool CheckForDisagreement()
        {
            List<SkillAssessment> skills;
            using (PerformanceAssessmentDB db = new PerformanceAssessmentDB())
            {
                skills = db.SkillAssessments.Where(s => s.IdAssessment == this.ID).ToList();
            }
            if (skills != null && skills.Count > 0)
            {
                foreach (SkillAssessment skillAssessment in skills)
                {
                    if (Math.Abs(skillAssessment.EmployeeScore.Value - skillAssessment.ManagerScore.Value) > 1)
                        return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Checks if the specified user has access to the assessment
        /// </summary>
        /// <param name="user">User</param>
        /// <returns>False if it has access</returns>
        public bool AccessDenied(UserModel user)
        {
            return ((user.UserType == UserType.Employee && this.IdEmployee != user.ID) ||
                (user.UserType == UserType.Manager && this.IdManager != user.ID));
        }

        /// <summary>
        /// Validates that everything was added before finishing the assessment for one user
        /// </summary>
        /// <param name="userType">User type</param>
        /// <returns></returns>
        public AssessmentValidationResult ValidateAssessment(UserType userType)
        {
            List<SkillAssessment> skills;
            List<Goal> goals;
            //Obtains goals and skills depending on the user type
            using (PerformanceAssessmentDB db = new PerformanceAssessmentDB())
            {
                skills = db.SkillAssessments.Where(s => s.IdAssessment == this.ID).ToList();
                if (userType == UserType.Employee)
                {
                    goals = db.Goals.Where(g => g.IdAssessment == this.ID).ToList();
                }
                else
                {
                    goals = (from g in db.Goals
                             join a in db.Assessments on g.IdAssessment equals a.ID
                             join p in db.AssessmentPeriods on a.IdAssessmentPeriod equals p.IdPreviousAssessmentPeriod
                             where p.ID == this.IdAssessmentPeriod && a.IdEmployee == this.IdEmployee
                             select g).ToList();
                }
            }

            //Validate that all the scores, comments and goals were filled
            if (userType == UserType.Employee)
            {
                foreach (SkillAssessment skillAssessment in skills)
                {
                    if (skillAssessment.EmployeeScore == null || string.IsNullOrEmpty(skillAssessment.EmployeeComments))
                        return AssessmentValidationResult.MissingScores;
                }
                foreach (Goal goal in goals)
                {
                    if (string.IsNullOrEmpty(goal.AttainableDescription) || string.IsNullOrEmpty(goal.MeasurableDescription) ||
                        string.IsNullOrEmpty(goal.RelevantDescription) || string.IsNullOrEmpty(goal.SpecificDescription) ||
                        string.IsNullOrEmpty(goal.TimelyDescription))
                        return AssessmentValidationResult.MissingGoals;
                }
            }
            else
            {
                foreach (SkillAssessment skillAssessment in skills)
                {
                    if (skillAssessment.ManagerScore == null || string.IsNullOrEmpty(skillAssessment.ManagerComments))
                        return AssessmentValidationResult.MissingScores;
                }
                if (goals != null)
                {
                    foreach (Goal goal in goals)
                    {
                        if (string.IsNullOrEmpty(goal.Comments))
                            return AssessmentValidationResult.MissingGoals;
                    }
                }
            }

            return AssessmentValidationResult.Valid;
        }

        #region State related methods

        /// <summary>
        /// Checks if the user controls should be available depending on the user type and state of the assessment
        /// </summary>
        /// <param name="userType">User type</param>
        /// <param name="typeAllowed">Allowed type</param>
        /// <returns>True if edition is allowed</returns>
        public bool EditionAllowed(UserType userType, params UserType[] typesAllowed)
        {
            if (typesAllowed.Contains(userType))
            {
                switch (userType)
                {
                    case UserType.Employee:
                        return AllowEmployeeEdition();
                    case UserType.Manager:
                        return AllowManagerEdition();
                    case UserType.Administrator:
                        return AllowAdminEdition();
                }
            }
            return false;
        }

        private bool AllowEmployeeEdition()
        {
            return this.IdAssessmentStatus == (int)AssessmentStatus.AutoEvaluationOpened ||
                this.IdAssessmentStatus == (int)AssessmentStatus.Disagreement;
        }

        private bool AllowManagerEdition()
        {
            return this.IdAssessmentStatus == (int)AssessmentStatus.AutoEvaluationApproved;
        }

        private bool AllowAdminEdition()
        {
            return this.IdAssessmentStatus == (int)AssessmentStatus.AutoEvaluationCompleted ||
                this.IdAssessmentStatus == (int)AssessmentStatus.DisagreementSolved ||
                this.IdAssessmentStatus == (int)AssessmentStatus.ManagerAssessmentCompleted;
        }
        #endregion

        public EmailUtil GetManagerEvaluationOpenEmail()
        {
            EmailUtil email = new EmailUtil();
            email.To.Add(this.Manager.Email);
            email.Subject = string.Format("Performance Assessment Opened - {0} {1}", this.Employee.FirstName, this.Employee.FirstLastName);
            email.Body = string.Format("<p>The Performance Assessment for Employee <strong>{0} {1}</strong> for the Period <strong>{2}</strong> is now available. To open this assessment go to the following link:</p><br /><a href=\"{3}/Assessments/Details?assessmentId={4}\">Go to assessment!</a>",
                this.Employee.FirstName,
                this.Employee.FirstLastName,
                this.AssessmentPeriod.Description,
                HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority),
                this.ID);
            email.IsBodyHtml = true;
            return email;
        }

        public EmailUtil GetAutoevaluationRejectEmail(string reason)
        {
            EmailUtil email = new EmailUtil();
            email.To.Add(this.Employee.Email);
            email.Subject = string.Format("Autoevaluation Rejected - {0}", this.AssessmentPeriod.Description);
            email.Body = string.Format("<p>The administrator has rejected your autoevaluation for Period <strong>{0}</strong> for the following reason: <br/><br/> {1}<br/><br/> Please go to the following link and make the necessary corrections:</p><br /><a href=\"{2}/Assessments/Details?assessmentId={3}\">Go to assessment!</a>",
                this.AssessmentPeriod.Description,
                reason,
                HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority),
                this.ID);
            email.IsBodyHtml = true;
            return email;
        }

        public EmailUtil GetManagerEvaluationRejectEmail(string reason)
        {
            EmailUtil email = new EmailUtil();
            email.To.Add(this.Manager.Email);
            email.Subject = string.Format("Performance Assessment Rejected - {0} {1}", this.Employee.FirstName, this.Employee.FirstLastName);
            email.Body = string.Format("<p>The administrator has rejected your evaluation for Employee <strong>{0} {1}</strong> for the Period <strong>{2}</strong> due to:<br/><br/> {3}<br/><br/> Please go to the following link and make the necessary corrections:</p><br /><a href=\"{4}/Assessments/Details?assessmentId={5}\">Go to assessment!</a>",
                this.Employee.FirstName,
                this.Employee.FirstLastName,
                this.AssessmentPeriod.Description,
                reason,
                HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority),
                this.ID);
            email.IsBodyHtml = true;
            return email;
        }

        public EmailUtil GetAutoevaluationCompleteEmail()
        {
            EmailUtil email = new EmailUtil();
            string[] userNames = Roles.GetUsersInRole("Administrator");
            foreach (string user in userNames)
            {
                MembershipUser mUser = Membership.GetUser(user);
                email.To.Add(mUser.Email);
            }

            email.Subject = string.Format("Autoevaluation Finished - {0} {1} ", this.Employee.FirstName, this.Employee.FirstLastName);
            email.Body = string.Format("<p>The employee <strong>{0} {1}</strong> has completed his auto evaluation for period <strong>{2}</strong>. To review this evaluation go to the following link:</p><br /><a href=\"{3}/Assessments/Details?assessmentId={4}\">Go to assessment!</a>",
                this.Employee.FirstName,
                this.Employee.FirstLastName,
                this.AssessmentPeriod.Description,
                HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority),
                this.ID);
            email.IsBodyHtml = true;
            return email;
        }

        public EmailUtil GetManagerEvaluationCompleteEmail()
        {
            EmailUtil email = new EmailUtil();
            string[] userNames = Roles.GetUsersInRole("Administrator");
            foreach (string user in userNames)
            {
                MembershipUser mUser = Membership.GetUser(user);
                email.To.Add(mUser.Email);
            }

            email.Subject = string.Format("Manager Evaluation Finished - {0} {1} ", this.Employee.FirstName, this.Employee.FirstLastName);
            email.Body = string.Format("<p>The manager <strong>{0} {1}</strong> has finished evaluating employee <strong>{2} {3}</strong> for period <strong>{4}</strong>. To review this evaluation go to the following link:</p><br /><a href=\"{5}/Assessments/Details?assessmentId={6}\">Go to assessment!</a>",
                this.Manager.FirstName,
                this.Manager.LastName,
                this.Employee.FirstName,
                this.Employee.FirstLastName,
                this.AssessmentPeriod.Description,
                HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority),
                this.ID);
            email.IsBodyHtml = true;
            return email;
        }

        public EmailUtil GetDissagreementFoundEmail()
        {
            EmailUtil email = new EmailUtil();
            email.To.Add(this.Employee.Email);
            email.Subject = string.Format("Disagreement Found for Assessment - {0}", this.AssessmentPeriod.Description);
            email.Body = string.Format("<p>Your manager has finished your performance assessment for Period <strong>{0}</strong> and a dissagreement in one or more Skill scores has been found. Please review and change this evaluation to make sure there is not a difference of more than one point between your auto evaluation and your manager's evaluation in any of the Skills. <br />To check your assessment go to the following link:</p><br /><a href=\"{1}/Assessments/Details?assessmentId={2}\">Go to assessment!</a>",
                this.AssessmentPeriod.Description,
                HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority),
                this.ID);
            email.IsBodyHtml = true;
            return email;
        }

        public EmailUtil GetDissagreementSolvedEmail()
        {
            EmailUtil email = new EmailUtil();
            string[] userNames = Roles.GetUsersInRole("Administrator");
            foreach (string user in userNames)
            {
                MembershipUser mUser = Membership.GetUser(user);
                email.To.Add(mUser.Email);
            }

            email.Subject = string.Format("Disagreement Solved - {0} {1}", this.Employee.FirstName, this.Employee.FirstLastName);
            email.Body = string.Format("<p>The employee <strong>{0} {1}</strong> has changed his auto evaluation for period <strong>{2}</strong> and solved the existing disagreement. To review this evaluation go to the following link:</p><br /><a href=\"{3}/Assessments/Details?assessmentId={4}\">Go to assessment!</a>",
                this.Employee.FirstName,
                this.Employee.FirstLastName,
                this.AssessmentPeriod.Description,
                HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority),
                this.ID);
            email.IsBodyHtml = true;
            return email;
        }

        public static string GetStatusName(AssessmentStatus status)
        {
            switch (status)
            {
                case AssessmentStatus.AutoEvaluationOpened:
                    return "Auto evaluation started";
                case AssessmentStatus.AutoEvaluationCompleted:
                    return "Auto evaluation submitted";
                case AssessmentStatus.AutoEvaluationApproved:
                    return "Auto evaluation approved by admin";
                case AssessmentStatus.ManagerAssessmentCompleted:
                    return "Manager assessment submitted";
                case AssessmentStatus.Disagreement:
                    return "Disagreement found";
                case AssessmentStatus.DisagreementSolved:
                    return "Disagreement solved and waiting for approval";
                case AssessmentStatus.Completed:
                    return "Completed";
            }
            return string.Empty;
        }
    }
}